https://stackoverflow.com/questions/64597525/r-magick-square-crop-and-circular-mask

https://www.r-bloggers.com/2016/11/extracting-exif-data-from-photos-using-r/

Estimating Canopy Cover with Spherical Photos

This tutorial was developed with the support of Althouse & Meade inc., and many of the ideas in the tutorial are based on ideas found here were synthesized by Kyle Nessen and collaborators.

Here we aim to evaluate the potential use of spherical photos for the estimation of canopy cover. The goal is to eventually apply this towards analyzing the habitat of monarchs in local eucalyptus groves. Traditionally this is done with a class of tools called densiometers. These tools can be biased, and other tools have been developed to overcome these biases like the GRS Densiometer. Similarly, methods using LiDAR and Photogrammetry (Andersen, McGaughey, and Reutebuch (2005) ), however LiDAR and other remote sensing platforms are not always available or affordable.

It is for that reason that this tutorial will aim at finding a more universal solution. Spherical cameras are small, portable, and relatively inexpensive, many offering built in GPS units at much smaller costs than bulkier hemispherical cameras. Much of the framework for this process was developed by Andis Arietta who also released several open license tools for conversions online (Arietta (2020) ).

library(leaflet) #for interactive webmaps
library(sf)
library(magick) #image manipulation
library(imager)
library(plotrix)
library(hemispheR)

Data Capture

Data was captured by me (Sam Ericksen) using a GoPro 360 camera on a 6’ pole while walking through the Coastal Access Monarch Butterfly Preserve in Los Osos, California. The area, can be seen here:

capture occurred over the course of a few hours, and involved grid lining out the northern grove on foot. The path of capture can be seen below.

tracks <- read_sf("PathShape/Euc_Forest_path.shp")

tracks <- st_zm(tracks) #drop z dimension

leaflet(tracks$geometry) %>% 
  addProviderTiles("Esri.WorldImagery") %>% 
  addPolylines()

Reading in Some Test Pictures

For ease of looping through large numbers of pictures, the first in the function chain will be built to take in a file path to an image, If RAM is not of concern for the number of images you are working with you may wish to read all images into memory using image_read() , and create a function to take in the magick images. For the sake of example, we will simply read in four 360 images.

img_paths <- paste0("360_Pics/", list.files("360_Pics"))

num_pics <- length(img_paths)

rand_ind <- sample.int(num_pics, 2) #save index for later to continue to work with the same pictures

test_img_paths <-img_paths[rand_ind] #grab 2 random 360 pictures from the picture directory

Cutting Images in Half

360 degree photos are in essence spherical photos, and they are captured as planar (rectangular) projections of a sphere. This means that to create a rectangular projection of a hemisphere, we must cut the spherical projection in half along its horizontal axis. Eventually all of these functions will be applied to thousands of photos, so everything will be created in functions, before it is applied to photos, so we don’t read thousands of photos into the ram all at once.

sphere_to_hemi_rect <- function(image_path){
  #'input: path to spherical image
  #'output: rectangular projection of hemisphere image
  sphere_rect_img <- image_read(image_path)
  
  hemi_rect <- image_crop(sphere_rect_img, "100%x50%") #crop off bottom half of image
  
  return(hemi_rect)
}

rect_imgs <- sphere_to_hemi_rect(test_img_paths)

image_append( rect_imgs, stack = TRUE)

Conversion to Polar Coordinates

That function will output a hemispherical photo that is projected on a cartesian coordinate system (rectangular), but we need polar coordinates to match to get rid of distortions from projection. Here the function takes in a rectangular hemispherical photo, and outputs a polar (circular) hemispherical photo.

hemi_rect_to_polar <- function(hemi_rect_image){
  
  #project to polar coordinates
  hemi_polar <- image_distort(hemi_rect_image,
                                   "Polar",
                                   c(0),
                                   bestfit = TRUE)
  
  #draw bounding 
  png(temp <- tempfile(fileext = ".png"), 736, 736)
  par(mar = rep(0,4), yaxs="i", xaxs="i")
  plot(0, type = "n", ylim = c(0,1), xlim=c(0,1), axes=F, xlab=NA, ylab=NA)
  plotrix::draw.circle(.5,0.5,.5, col="black")
  
  dev.off()
  
  mask <-  image_read(temp) %>% 
    image_scale(as.character(image_info(hemi_polar)$width))
  
  unlink(temp)
  
  hemi_polar <- image_composite(mask, hemi_polar, "minus")
  
  return(hemi_polar)

}

hemi_imgs <- hemi_rect_to_polar(rect_imgs)

image_append( hemi_imgs)

Write to directory

Now we want to create a directory to save our resultant hemispherical photos in this will be the intermediate output. Here we will have successfully created hemispherical photos that mimic the quality of expensive hemispherical cameras using a relatively inexpensive 360 degree camera, congratulations!

pol_pic_dir <- "hemi_polor_pics/"

if (!dir.exists(pol_pic_dir)){ #only create it if it hasnt alread been done
  
  dir.create(pol_pic_dir)
  
  all_hemi_rects <-  sphere_to_hemi_rect(img_paths)
  
  all_polar <-  hemi_rect_to_polar(all_hemi_rects)
  
  img_names <- sapply(strsplit(img_paths,"/"), getElement, 2)
  
  for (img_ind in 1:length(all_polar)){
    write_to <- paste0(pol_pic_dir, img_names[img_ind])
    
    # print(paste0(img_ind, "/", length(all_polar))) #for progress through loop
    
    image_write(
      image = all_polar[img_ind], 
      path = write_to)
  }
  
  
}

Light Indices

Next we’ll use an experimental package called hemispheR to get some canopy information from the hemispherical images ( Chianucci and Macek (2022) ).

References

Andersen, Hans-Erik, Robert J. McGaughey, and Stephen E. Reutebuch. 2005. “Estimating Forest Canopy Fuel Parameters Using LIDAR Data.” Remote Sensing of Environment 94 (4): 441–49. https://doi.org/10.1016/j.rse.2004.10.013.
Arietta, A. Z. Andis. 2020. “Evaluation of Hemispherical Photos Extracted from Smartphone Spherical Panorama Images to Estimate Canopy Structure and Forest Light Environment.” https://www.biorxiv.org/content/10.1101/2020.12.15.422956v2.
Chianucci, Francesco, and Martin Macek. 2022. “hemispheR: An R Package for Fisheye Canopy Image Analysis.” bioRxiv. https://doi.org/10.1101/2022.04.01.486683.